/**
 * AplProjeto.java
 *
 * Projeto: SGA-LEDS
 *
 * Rodrigo Soares Maia IFES - Instituto Federal do Espírito Santo - Campus Serra
 */
package controleAtividades.cgt;

import controleAtividades.cdp.Atividade;
import controleInterno.cdp.CategoriaTipo;
import controleInterno.cdp.Colaborador;
import controleAtividades.cdp.ColaboradorAtividade;
import controleAtividades.cdp.NivelAtividade;
import controleInterno.cdp.Prioridade;
import controleInterno.cdp.Projeto;
import controleAtividades.cdp.RegistroAtividade;
import controleAtividades.cdp.StatusAtividade;
import controleInterno.cdp.StatusProjeto;
import controleInterno.cdp.TipoAtividade;
import controleInterno.cdp.TipoProjeto;
import controleAtividades.cgd.AtividadeDAOJPA;
import controleAtividades.cgd.ColaboradorAtividadeDAOJPA;
import controleAtividades.cgd.RegistroAtividadeDAOJPA;
import controleInterno.cgd.TipoAtividadeDAOJPA;
import controleInterno.cgt.AplControleInterno;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.logging.Level;
import java.util.logging.Logger;
import utilitario.utilitarioPersistencia.DAO;
import utilitario.utilitarioPersistencia.DAOFactory;

public class AplProjeto
{
    private DAO apDaoProjeto = DAOFactory.obterDAO(Projeto.class);
    private DAO apDaoAtividade = DAOFactory.obterDAO(Atividade.class);
    private DAO apDaoColaboradorAtividade = DAOFactory.obterDAO(ColaboradorAtividade.class);
    private DAO apDaoRegistroAtividade = DAOFactory.obterDAO(RegistroAtividade.class);
    private DAO apDaoTipoAtividade = DAOFactory.obterDAO(TipoAtividade.class);
    private DAO apDaoCategoriaTipo = DAOFactory.obterDAO(CategoriaTipo.class);
    private static AplProjeto instance = null;

    public static AplProjeto getInstance()
    {
        if (instance == null)
        {
            instance = new AplProjeto();
        }
        return instance;
    }

    //ATIVIDADE
    
    public Atividade salvarAtividade(List<Object> atributos)
    {
        Atividade a = new Atividade();
            
        a.setDescricao((String) atributos.get(0));
        a.setDataInicio((Calendar) atributos.get(1));
        a.setDataFim((Calendar) atributos.get(2));
        a.setProgresso((Double) atributos.get(3));
        a.setDuracao((Double) atributos.get(4));
        a.setTipo((TipoAtividade) atributos.get(5));
        //TODO calcular status da atividade baseado nas datas
        a.setStatus(StatusAtividade.NAO_INICIADA);
        
        a.setResponsavel((Colaborador) atributos.get(6));
        
        a.setProjeto((Projeto) atributos.get(7));
        
        a.setAtividadePai((Atividade) atributos.get(8));
        
        try
        {
            a = (Atividade) apDaoAtividade.salvar(a);
        }
        catch (Exception ex)
        {
            Logger.getLogger(AplControleInterno.class.getName()).log(Level.SEVERE, null, ex);
        }

        return a;
    }
    
    public Atividade salvarAtividade(Map atributos)
    {
        Atividade a = new Atividade();     
        
        a.setDescricao((String) atributos.get("descricao"));
        a.setDataInicio((Calendar) atributos.get("dataInicio"));
        a.setDataFim((Calendar) atributos.get("dataFim"));
        a.setProgresso((Double) atributos.get("progresso"));
        a.setDuracao((Double) atributos.get("duracao"));
        a.setTipo((TipoAtividade) atributos.get("tipo"));
        //TODO calcular status da atividade baseado nas datas
        a.setStatus(StatusAtividade.NAO_INICIADA);
        a.setNivel((NivelAtividade) atributos.get("nivel"));
        a.setResponsavel((Colaborador) atributos.get("responsavel"));
        
        a.setProjeto((Projeto) atributos.get("projeto"));
        
        a.setAtividadePai((Atividade) atributos.get("atividadePai"));
        
        try
        {
            a = (Atividade) apDaoAtividade.salvar(a);
        }
        catch (Exception ex)
        {
            Logger.getLogger(AplControleInterno.class.getName()).log(Level.SEVERE, null, ex);
        }

        return a;
    }
    
    public Atividade alterarAtividade(Atividade a)
    {
        try
        {
            return (Atividade) apDaoAtividade.salvar(a);
        }
        catch (Exception ex)
        {
            Logger.getLogger(AplProjeto.class.getName()).log(Level.SEVERE, null, ex);
        }
        
        return null;
    }

    public Atividade alterarAtividade(Atividade a, List<Colaborador> novosAlocados)
    {
        try
        {           
            //obtendo todos os alocados para a atividade em questao
            List<Colaborador> atuais = this.obterColaboradoresAlocados(a);
            
            if (this.verificaAlocados(atuais, novosAlocados))
            {
                a = (Atividade) apDaoAtividade.salvar(a);
                
                this.realocarColaboradores(a, novosAlocados);
                
                return a;
            }

            this.salvarAlocados(novosAlocados, a);
            
        }
        catch (Exception ex)
        {
            Logger.getLogger(AplControleInterno.class.getName()).log(Level.SEVERE, null, ex);
        }

        return null;
    }

    public boolean apagarAtividade(Atividade a)
    {
        List<ColaboradorAtividade> listaC;
        try
        {
            //TODO fazer validacao antes
            if (!this.temRegistros(a))
            {
                //excluindo todos alocados para a atividade em questao
                listaC = this.obterAlocados(a);
                
                for (ColaboradorAtividade ca : listaC)
                {
                    this.apagarColaboradorAtividade(ca);
                }
                
                apDaoAtividade.excluir(a);
                return true;
            }
        }
        catch (Exception ex)
        {
            Logger.getLogger(AplControleInterno.class.getName()).log(Level.SEVERE, null, ex);
        }
        
        return false;
    }
    
    public boolean apagarAtividadeCascata(Atividade a)
    {
        //se alguem tem registro, retorna false sem apagar ninguem
        if (temRegistrosCascata(a))
        {
            return false;
        }
        else
        {
            List<Atividade> filhas = this.obterFilhas(a);

            if (filhas.isEmpty())
            {
                if (this.apagarAtividade(a)) return true;
                else return false;
            }
            else
            {
                for (Atividade filha : filhas)
                {
                    apagarAtividadeCascata(filha);
                }

                apagarAtividade(a);
                return true;
            }
        }
    }
    
    public boolean temRegistrosCascata(Atividade a)
    {
        List<Atividade> filhas = this.obterFilhas(a);
        
        //se nao tiver filhas
        if (filhas.isEmpty())
        {
            //verifica se a propria atividade tem registros
            if (temRegistros(a)) return true; //se tiver, retorna true
        }
        else //se tiver filhas
        {
            //para cada filha
            for (Atividade filha : filhas)
            {
                //verifica se alguma delas, ou a filha delas, tem registro
                if (temRegistrosCascata(filha)) return true; // se tiver, retorna true
                else return false;
            }
        }
        
        return false;
    }
    
    public boolean temRegistros(Atividade a)
    {
        List<ColaboradorAtividade> listaC;
        if (this.obterRegistros(a).isEmpty())
        {
            return false;
        }
        else return true;
    }

    public List<Atividade> obterAtividades()
    {
        try
        {
            return (List<Atividade>) apDaoAtividade.obter(Atividade.class);
        }
        catch (Exception ex)
        {
            Logger.getLogger(AplControleInterno.class.getName()).log(Level.SEVERE, null, ex);
        }

        return null;
    }
    
    public List<Atividade> obterAtividades(Projeto p)
    {
        try
        {
            return ((AtividadeDAOJPA) apDaoAtividade).obter(p);
        }
        catch (Exception ex)
        {
            Logger.getLogger(AplControleInterno.class.getName()).log(Level.SEVERE, null, ex);
        }

        return null;
    }
    
    public List<Atividade> obterAtividadesGeral(Projeto p)
    {
        try
        {
            return ((AtividadeDAOJPA) apDaoAtividade).obterAtividadesGeral(p);
        }
        catch (Exception ex)
        {
            Logger.getLogger(AplControleInterno.class.getName()).log(Level.SEVERE, null, ex);
        }

        return null;
    }
    
    public List<Atividade> obterAtividadesIndividual(Projeto p, Colaborador c)
    {
        try
        {
            return ((AtividadeDAOJPA) apDaoAtividade).obterAtividadesIndividual(p, c);
        }
        catch (Exception ex)
        {
            Logger.getLogger(AplControleInterno.class.getName()).log(Level.SEVERE, null, ex);
        }

        return null;
    }
    
    /**
     * Retorna a primeira atividade pai de nivel GERAL que for encontrada
     * @param a
     * @return 
     */
    public Atividade obterPaiGeral(Atividade a)
    {
        if (a.getAtividadePai().getNivel() == NivelAtividade.GERAL)
        {
            return a.getAtividadePai();
        }
        else
        {
            return obterPaiGeral(a.getAtividadePai());
        }
    }
    
    public List<Atividade> obterFilhas(Atividade a)
    {
        try
        {
            return ((AtividadeDAOJPA) apDaoAtividade).obterFilhas(a);
        }
        catch (Exception ex)
        {
            Logger.getLogger(AplControleInterno.class.getName()).log(Level.SEVERE, null, ex);
        }

        return null;
    }
    
    public List<Atividade> obterFilhasGeral(Atividade a)
    {
        try
        {
            return ((AtividadeDAOJPA) apDaoAtividade).obterFilhasGeral(a);
        }
        catch (Exception ex)
        {
            Logger.getLogger(AplControleInterno.class.getName()).log(Level.SEVERE, null, ex);
        }

        return null;
    }
    
    public List<Atividade> obterFilhasIndividual(Atividade a, Colaborador c)
    {
        try
        {
            return ((AtividadeDAOJPA) apDaoAtividade).obterFilhasIndividual(a, c);
        }
        catch (Exception ex)
        {
            Logger.getLogger(AplControleInterno.class.getName()).log(Level.SEVERE, null, ex);
        }

        return null;
    }
    
    public List<Atividade> obterFilhasGrupo(Atividade a, Colaborador c)
    {
        try
        {
            return ((AtividadeDAOJPA) apDaoAtividade).obterFilhasGrupo(a, c);
        }
        catch (Exception ex)
        {
            Logger.getLogger(AplControleInterno.class.getName()).log(Level.SEVERE, null, ex);
        }

        return null;
    }
    
    public void trocaPai(Atividade atividade, Atividade novoPai)
    {
        atividade.setAtividadePai(novoPai);
        //this.alterarAtividade(atividade, atividade.getAlocados()); //TODO o pai interfere nos alocados??
        this.alterarAtividade(atividade);
    }
    
    
    public void alterarPais(Atividade a, double diferenca)
    {
        Atividade pai = a.getAtividadePai();
        Atividade ultimoPai = null;
            
        //adaptando os pais do mesmo nivel as novas datas...
        while (pai != null)
        {
            if (a.getNivel() == pai.getNivel())
            {
                if (pai.getDataInicio().after(a.getDataInicio()))
                {
                    pai.setDataInicio(a.getDataInicio());
                }

                if (pai.getDataFim().before(a.getDataFim()))
                {
                    pai.setDataFim(a.getDataFim());
                }
                
                pai.setDuracao(diferenca);

                this.alterarAtividade(pai);
            }
            else
            {
                break;
            }
            
            pai = pai.getAtividadePai();
        }
    }
    
    //COLABORADOR_ATIVIDADE
    
    public List<ColaboradorAtividade> salvarAlocados(List<Colaborador> colaboradores, Atividade atividade)
    {
        ColaboradorAtividade ca;
        List<ColaboradorAtividade> alocados = null;
        
        if (colaboradores != null)
        {
            alocados = new ArrayList<>();
            for (Colaborador c : colaboradores)
            {
                try
                {
                    ca = new ColaboradorAtividade(c, atividade);
                    ca = (ColaboradorAtividade) apDaoColaboradorAtividade.salvar(ca);

                    alocados.add(ca);
                }
                catch (Exception ex)
                {
                    Logger.getLogger(AplControleInterno.class.getName()).log(Level.SEVERE, null, ex);
                }
            }
        }
        
        return alocados;
    }
    
    public List<ColaboradorAtividade> obterAlocados(Atividade a)
    {
        try
        {
            return ((ColaboradorAtividadeDAOJPA) apDaoColaboradorAtividade).obter(a);
        }
        catch (Exception ex)
        {
            Logger.getLogger(AplControleInterno.class.getName()).log(Level.SEVERE, null, ex);
        }

        return null;
    }
    
    public List<Colaborador> obterColaboradoresAlocados(Atividade a)
    {
        try
        {
            List<ColaboradorAtividade> lista =  ((ColaboradorAtividadeDAOJPA) apDaoColaboradorAtividade).obter(a);
            List<Colaborador> colaboradores = new ArrayList<>();
            
            for (ColaboradorAtividade ca : lista)
            {
                colaboradores.add(ca.getColaborador());
            }
            
            return colaboradores;
        }
        catch (Exception ex)
        {
            Logger.getLogger(AplControleInterno.class.getName()).log(Level.SEVERE, null, ex);
        }

        return null;
    }
    
    public List<Colaborador> obterColaboradoresGrupo(Atividade a)
    {
        Atividade paiGeral = this.obterPaiGeral(a);
        
        return this.obterColaboradoresAlocados(paiGeral);
    }
    
    public boolean apagarColaboradorAtividade(ColaboradorAtividade ca)
    {
        try
        {
            //TODO validacao aqui?
            apDaoColaboradorAtividade.excluir(ca);
        }
        catch (Exception ex)
        {
            Logger.getLogger(AplProjeto.class.getName()).log(Level.SEVERE, null, ex);
        }
        
        return true;
    }

    public boolean verificaAlocados(List<Colaborador> atuais, List<Colaborador> novos)
    {
        for (Colaborador c : atuais)
        {
            //verificando se alguem foi retirado (ou seja, se ele nao estiver na lista dos novos alocados)
            if (!novos.contains(c))
            {
                //se foi, verifica se o cara "tinha" registro
                if (!this.obterRegistros(c).isEmpty())
                {
                    //se tinha, ERRO! //TODO ver um jeito de mostrar para o usuario quais os colaboradores com registro que foram tirados da alocacao
                    //break;
                    return false;
                }
            }
        }
        
        return true;
    }
    
    /**
     * 
     * @param a
     * @param novosAlocados
     * @return 
     */
    public List<ColaboradorAtividade> realocarColaboradores(Atividade a, List<Colaborador> novosAlocados)
    {
        List<ColaboradorAtividade> listaNovos = new ArrayList<>();
        List<ColaboradorAtividade> listaAntigos = this.obterAlocados(a);
        
        ColaboradorAtividade novo;
        
        //percorre a lista de antigos colaboradores (que jah estavam alocados para a atividade)
        for (ColaboradorAtividade ca : listaAntigos)
        {
            //se o colaborador estiver tambem na lista de novos (ou seja, que continuarah alocado), ele eh removido da lista, pois jah existe um objeto para ele
            if (novosAlocados.contains(ca.getColaborador()))
            {
                novosAlocados.remove(ca.getColaborador());
            }
            else //se ele nao estiver, a relacao eh apagado do bd 
            {
                this.apagarColaboradorAtividade(ca);
            }
        }
        
        //percorrendo a lista dos que sobraram para que sejam criados novos objetos
        for (Colaborador c : novosAlocados)
        {
            novo = new ColaboradorAtividade(c, a);
            
            try
            {
                apDaoColaboradorAtividade.salvar(novo);
            }
            catch (Exception ex)
            {
                Logger.getLogger(AplProjeto.class.getName()).log(Level.SEVERE, null, ex);
            }
            
            listaNovos.add(novo);
        }
        
        return listaNovos;
    }
    
    public List<ColaboradorAtividade> obterColaboradorAtividade(Atividade a, Colaborador c)
    {
        try
        {
            List<ColaboradorAtividade> lista =  ((ColaboradorAtividadeDAOJPA) apDaoColaboradorAtividade).obter(a, c);
  
            return lista;
        }
        catch (Exception ex)
        {
            Logger.getLogger(AplControleInterno.class.getName()).log(Level.SEVERE, null, ex);
        }

        return null;
    }
    
    public boolean ehAlocado(Atividade a, Colaborador c)
    {
        List<Colaborador> alocados = this.obterColaboradoresAlocados(a);
        
        if (alocados.contains(c))
        {
            return true;
        }
        
        return false;
    }
    
    //REGISTRO
    
    public RegistroAtividade salvarRegistro(Map atributos)
    {
        RegistroAtividade r = new RegistroAtividade();     
        
        Colaborador c = (Colaborador) atributos.get("colaborador");
        Atividade a = (Atividade) atributos.get("atividade");
        
        List<ColaboradorAtividade> listCa = (List<ColaboradorAtividade>) ((ColaboradorAtividadeDAOJPA) apDaoColaboradorAtividade).obter(a, c);
        
        ColaboradorAtividade ca = listCa.get(0);
        
        r.setDescricao((String) atributos.get("descricao"));
        r.setColaborador(ca);
        r.setDia((Calendar) atributos.get("dia"));
        r.setHoraInicio((Calendar) atributos.get("horaInicio"));
        r.setHoraFim((Calendar) atributos.get("horaFim"));
        //TODO calcular intervalo de tempo entre as datas...
        r.setDuracao((Double) atributos.get("duracao"));
        
        try
        {
            r = (RegistroAtividade) apDaoRegistroAtividade.salvar(r);
        }
        catch (Exception ex)
        {
            Logger.getLogger(AplControleInterno.class.getName()).log(Level.SEVERE, null, ex);
        }

        return r;
    }
    
    public RegistroAtividade alterarRegistro(RegistroAtividade r)
    {
        try
        {
            return (RegistroAtividade) apDaoRegistroAtividade.salvar(r);
        }
        catch (Exception ex)
        {
            Logger.getLogger(AplControleInterno.class.getName()).log(Level.SEVERE, null, ex);
        }

        return null;
    }
    
    public List<RegistroAtividade> obterRegistros(Colaborador c)
    {
        try
        {
            return ((RegistroAtividadeDAOJPA) apDaoRegistroAtividade).obter(c);
        }
        catch (Exception ex)
        {
            Logger.getLogger(AplControleInterno.class.getName()).log(Level.SEVERE, null, ex);
        }

        return null;
    }
    
    public List<RegistroAtividade> obterRegistros(Projeto p, Colaborador c)
    {
        try
        {
            return ((RegistroAtividadeDAOJPA) apDaoRegistroAtividade).obter(p, c);
        }
        catch (Exception ex)
        {
            Logger.getLogger(AplControleInterno.class.getName()).log(Level.SEVERE, null, ex);
        }

        return null;
    }
    
    public Double obterHorasProjeto(Projeto p)
    {
        Double horas = 0.0;
        try
        {
            List<RegistroAtividade> registros = (List<RegistroAtividade>) apDaoRegistroAtividade.obter(RegistroAtividade.class);
            
            for (RegistroAtividade registroAtividade : registros)
            {
                horas += registroAtividade.getDuracao();
            }
            
            return horas;
        }
        catch (Exception ex)
        {
            Logger.getLogger(AplProjeto.class.getName()).log(Level.SEVERE, null, ex);
        }
        
        return null;
    }
    
    public Map obterRegistros(Map atributos)
    {
        List<RegistroAtividade> registros;
        Double totalHoras = 0.0;
        
        Map resultados = new HashMap();
        
        Calendar inicio = (Calendar) atributos.get("dataInicio");
        Calendar fim = (Calendar) atributos.get("dataFim");
        
        Projeto p = (Projeto) atributos.get("projeto");
        Colaborador c = (Colaborador) atributos.get("colaborador");
        TipoAtividade t = (TipoAtividade) atributos.get("tipoAtividade");
        
        try
        {
            //registros = ((RegistroAtividadeDAOJPA) apDaoRegistroAtividade).obter(p, c, t, inicio, fim);
            
            
            if (inicio != null)
            {
                registros = ((RegistroAtividadeDAOJPA) apDaoRegistroAtividade).obter(inicio, fim);
            }
            else
            {
                registros = (List<RegistroAtividade>) apDaoRegistroAtividade.obter(RegistroAtividade.class);
            }
            if (p != null)
            {
                for (int i = 0; i < registros.size(); i++)
                {
                    RegistroAtividade registroAtividade = registros.get(i);
                    
                    if (registroAtividade.getColaborador().getAtividade().getProjeto() != p)
                    {
                        registros.remove(registroAtividade);
                        i--;
                    }
                }
            }
            if (c != null)
            {
                for (int i = 0; i < registros.size(); i++)
                {
                    RegistroAtividade registroAtividade = registros.get(i);
                    
                    if (registroAtividade.getColaborador().getColaborador() != c)
                    {
                        registros.remove(registroAtividade);
                        i--;
                    }
                }
            }
            if (t != null)
            {
               for (int i = 0; i < registros.size(); i++)
                {
                    RegistroAtividade registroAtividade = registros.get(i);
                    
                    if (registroAtividade.getColaborador().getAtividade().getTipo() != t)
                    {
                        registros.remove(registroAtividade);
                        i--;
                    }
                }
            }
            
            resultados.put("registros", registros);
            
            for (RegistroAtividade registroAtividade : registros)
            {
                totalHoras += registroAtividade.getDuracao();
            }
            
            resultados.put("horasConsulta", totalHoras);
            
            return resultados;
        }
        catch (Exception ex)
        {
            Logger.getLogger(AplControleInterno.class.getName()).log(Level.SEVERE, null, ex);
        }

        return null;
    }
    
    public List<RegistroAtividade> obterRegistros(ColaboradorAtividade ca)
    {
        try
        {
            return ((RegistroAtividadeDAOJPA) apDaoRegistroAtividade).obter(ca);
        }
        catch (Exception ex)
        {
            Logger.getLogger(AplControleInterno.class.getName()).log(Level.SEVERE, null, ex);
        }

        return null;
    }
    
    public List<RegistroAtividade> obterRegistros(Atividade a)
    {
        List<ColaboradorAtividade> listaC;
        List<RegistroAtividade> listaR = new ArrayList<>();
        
        try
        {
            listaC = this.obterAlocados(a);
            
            for (ColaboradorAtividade ca : listaC)
            {
                listaR.addAll(this.obterRegistros(ca));
            }
            
            return listaR;
            
            //return ((RegistroAtividadeDAOJPA) apDaoRegistroAtividade).obter(a); TODO consertar JPQL para simplificar a obtencao
        }
        catch (Exception ex)
        {
            Logger.getLogger(AplControleInterno.class.getName()).log(Level.SEVERE, null, ex);
        }

        return null;
    }
    
    public Double calcularHorasTrabalhadas(Projeto p, Colaborador c)
    {
        Double horas = 0.0;
        
        List<RegistroAtividade> registros = this.obterRegistros(p, c);
        
        for (RegistroAtividade r : registros)
        {
            horas += r.getDuracao();
        }
        
        return horas;
    }
}